/* Nate Deisinger (ndeisinger@wisc.edu)
 *
 * This file is part of soot-csi, a project to apply lightweight
 * instrumentation to Java programs to facilitate debugging through
 * the Soot analysis framework.
 *
 * Copyright (c) 2014, Nate Deisinger, University of Wisconsin-Madison
 *   Madison, Wisconsin 53715
 *   All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc.,
 *   51 Franklin Street, Fifth Floor,
 *   Boston, MA  02110-1301
 *   USA
 * or retrieve version 2.1 at their website:
 *   http://www.gnu.org/licenses/lgpl-2.1.html
 */
package uwmadison.soot.analysis;

/**
 * Auxiliary class to support call coverage data collection.
 * When the program throws an exception, a 'shadow stack' of these objects
 * will be generated as the stack unwinds.  
 * 
 * If the exception is caught and handled, the shadow stack will be freed on the
 * next successful function return.
 * 
 * This class is almost entirely the same as PathTrace; it would probably be 
 * best to, in the future, create a super "Trace" class and subclass that to
 * create these two classes.
 * 
 * @author Nate Deisinger
 */
public class CallTrace {
    public boolean[] cc_arr; //Making public saves getter/setter overhead
    protected CallTrace enclosing;
    protected CallTrace encloses;
    protected String methodName;
    public static boolean inUse; //Only do full work when crashing
    private static CallTrace innermost;
    private static CallTrace firstFree;
    private static CallTrace initial;
    private static final CallTrace dummyTrace;
    
    /**
     * Initial class setup to pre-generate entries.
     */
    static
    {
        //Perform setup - that is, generate 50 entries
        firstFree = new CallTrace();
        initial = firstFree;
        innermost = null;
        CallTrace currEnt = firstFree;
        for (int i = 0; i < 50; i++)
        {
            CallTrace newEnt = new CallTrace();     
            currEnt.encloses = newEnt;
            newEnt.enclosing = currEnt;
            currEnt = newEnt;
        }
        dummyTrace = new CallTrace();
        inUse = false;
    }
    
    /**
     * Constructs an empty CallTrace object.
     */
    public CallTrace() {
        methodName = "";
        enclosing = null;
        encloses = null;
        cc_arr = null;
        return;
    }

    /**
     * Constructs a CallTrace object with the
     * given method name and number of call sites.
     */
    public CallTrace(String methodName, int count) {
        this.methodName = methodName;
        enclosing = null;
        encloses = null;
        cc_arr = new boolean[count];
        return;
    }
    
    /**
     * Returns a dummy trace object.  Used for performance debugging.
     */
    public static CallTrace getRef_dummy(String name)
    {
        return dummyTrace;
    }
    
    /**
     * Return a reference to a new PathTrace object.  Generates
     * additional objects if all are used.
     */
    public static CallTrace getRef(String name, int count)
    {        
        if (firstFree == null)
        {         
            //Need to generate more entries; for now do 20
            CallTrace currEnt = innermost;
            for (int i = 0; i < 20; i++)
            {
                CallTrace newEnt = new CallTrace();     
                currEnt.encloses = newEnt;
                newEnt.enclosing = currEnt;
                currEnt = newEnt;
            }
            firstFree = innermost.encloses;
        }
        CallTrace myTrace = firstFree;
        firstFree = myTrace.encloses;
        myTrace.methodName = name;
        myTrace.cc_arr = new boolean[count];
        inUse = true;
        return myTrace;
    }
    
    /**
     * Converts an array of booleans to a string in format "val val val val[...]"
     */
    protected String convertArr(boolean[] arr)
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < arr.length; i++)
        {
            sb.append(arr[i] + " ");
        }
        sb.setLength(sb.length() - 1);
        return sb.toString();
    }

    /**
     * Dumps the local call coverage data of a single trace.
     */
    protected void dumpTrace()
    {
        System.err.println("Method name: " + methodName + " callArr: " + convertArr(cc_arr));
        return;
    }


    /**
     * Dummy method for performance testing.
     */
    public void close_dummy() {
        int l = 4 + 4;
        return;
    }

    /**
     * Re-initializes the stack for us.
     */
    public static void freeStack()
    {
        firstFree = initial;
        inUse = false;
    }

    /**
     * Makes this CallTrace object the last valid one.
     */
    public void makeTop() {
        //innermost = this;
        firstFree = this.encloses;
        return;
    }
    
    /**
     * Dumps all local and global call coverage data.
     */
    static public void dumpAllTraces() {
        System.err.println("Local call coverage:");
        for (CallTrace current = firstFree.enclosing;
                current != null; current = current.enclosing)
        {
            current.dumpTrace();
        }
        System.err.println("Global call coverage:");
        //Global call coverage data dump code
        //is added here by soot as we instrument.
        return;
    }
} 